package com.baturu.vin.gm.service.impl;

import com.baturu.kit.kit.guava2.Lists2;
import com.baturu.kit.kit.guava2.Sets2;
import com.baturu.vin.base.service.impl.SuperEPCAnalyser;
import com.baturu.vin.dto.EPCImagePointDTO;
import com.baturu.vin.dto.EPCPartsImageDTO;
import com.baturu.vin.dto.EPCPartsImagePositionDTO;
import com.baturu.vin.dto.superEPC.*;
import com.baturu.vin.dto.superEPC.entity.SEPCCarTypeQueryParam;
import com.baturu.vin.dto.superEPC.entity.SEPCGroupQueryParam;
import com.baturu.vin.dto.superEPC.entity.SEPCPartsQueryParam;
import com.baturu.vin.gm.dal.dao.*;
import com.baturu.vin.gm.dal.entity.GroupDescEntity;
import com.baturu.vin.gm.dal.entity.OptionDescEntity;
import com.baturu.vin.gm.dal.entity.SubjectDescEntity;
import com.baturu.vin.gm.dto.*;
import com.baturu.vin.gm.transformer.GMSEPCCarTypeTransformer;
import com.baturu.vin.gm.transformer.GMSEPCGroupTransformer;
import com.baturu.vin.gm.transformer.GMSEPCPartsTransformer;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @Author: chenjiahao
 * @Time: 2019/11/1
 */
@Slf4j
@Service("generalMotorsSuperEPCAnalyser")
public class GeneralMotorsSuperEPCAnalyser extends SuperEPCAnalyser {

    private final static int OFFSET = 35;

    @Autowired
    private GeneralMotorsEPCAnalyser generalMotorsEPCAnalyser;
    @Autowired
    private VinModelDAO vinModelDAO;
    @Autowired
    private OptionGroupDAO optionGroupDAO;
    @Autowired
    private GMGroupDAO gmGroupDAO;
    @Autowired
    private PartsDetailDAO partsDetailDAO;
    @Autowired
    private PartsPositionDAO partsPositionDAO;
    @Autowired
    private GmPartsMappingDAO partsMappingDAO;

    private Pattern yearOne = Pattern.compile("^\\d{4}.\\d{4}[\\s\\S]*$");
    private Pattern yearTwo = Pattern.compile("^\\d{4}[\\s\\S]*$");

    private Pattern seriesTwo = Pattern.compile("^[\\s\\S]{10}[a-zA-Z,， ]*[0-9]{2}[^0-9^-][\\S\\s]*$");
    private Pattern seriesOne = Pattern.compile("^[\\s\\S]{10}[a-zA-Z,， ]*[0-9]{3}[^0-9]{2}[\\S\\s]*$");
    private Pattern seriesThree = Pattern.compile("^[\\s\\S]{10}[a-zA-Z,， ]* [\\S\\s]*$");
    private Pattern seriesFour = Pattern.compile("^[\\s\\S]{10}[a-zA-Z\\-0-9 ]* [\\S\\s]*$");

    private Pattern bodyRex = Pattern.compile("[0-9]{2}-[0-9]{2}");

    private Pattern optionCodeRex = Pattern.compile("\\(.*?[A-Z0-9]{3}.*?\\)");
    private Pattern dealOptionCodeRex = Pattern.compile(".*[A-Z0-9]{3}.*");

    @Override
    public List<SEPCCarTypeDTO> querySuperEPCCarTypeByVinCode(SEPCCarTypeQueryParam param) {
        String vinCode = param.getVinCode();
        GMVinModelDTO gmVinModelDTO = generalMotorsEPCAnalyser.analyseVinCode(vinCode);
        if (gmVinModelDTO == null) {
            return Collections.emptyList();
        }
        String catalogCh = vinModelDAO.queryCatalogCh(gmVinModelDTO.getCatalog());
        gmVinModelDTO.setCatalogCN(gmVinModelDTO.getCatalog() + "-" + catalogCh);
        CarSystemBtrIdDTO carSystemBtrIdDTO = generalMotorsEPCAnalyser.buildCarType(gmVinModelDTO);
        if (Objects.isNull(carSystemBtrIdDTO)) {
            return Collections.emptyList();
        }
        gmVinModelDTO.setBtrId(carSystemBtrIdDTO.getBtrId());
        buildOptionCodeDes(gmVinModelDTO);
        return Lists.newArrayList(GMSEPCCarTypeTransformer.VIN_TO_SEPC_CAR_TYPE.safeApply(gmVinModelDTO));
    }

    private void buildOptionCodeDes(GMVinModelDTO gmVinModelDTO) {
        String optionCode = gmVinModelDTO.getOptionCode();
        if (StringUtils.isBlank(optionCode) || !optionCode.contains(",")) {
            return;
        }
        List<SEPCCarTypeDetailDTO> optionCodeDeses = getSEPCOptionCodeDes(gmVinModelDTO.getOptionCodeList());
        gmVinModelDTO.setOptionDesc(optionCodeDeses);
    }

    private List<SEPCCarTypeDetailDTO> getSEPCOptionCodeDes(List<String> optionCodeList) {
        List<OptionDescEntity> optionDescEntities = optionGroupDAO.queryOptionCodeDes(optionCodeList);
        return Lists2.transformNonNull(optionDescEntities, GMSEPCCarTypeTransformer.OPTION_CODE_DES);
    }

    @Override
    protected List<SEPCGroupDTO> queryGroupByVinCodeAndParentGroup(SEPCGroupQueryParam param) {
        String vinCode = param.getVinCode();
        GMVinModelDTO gmVinModelDTO = generalMotorsEPCAnalyser.analyseVinCode(vinCode);
        if (gmVinModelDTO == null) {
            return Collections.emptyList();
        }
        generalMotorsEPCAnalyser.buildCarType(gmVinModelDTO);
        SEPCGroupDTO groupDTO = param.getGroupDTO();
        if (groupDTO == null) {
            return queryMGroup(gmVinModelDTO);
        }
        return querySubGroup(gmVinModelDTO, groupDTO.getMainGroupCode());
    }

    private List<SEPCGroupDTO> querySubGroup(GMVinModelDTO gmVinModelDTO, String mainGroupCode) {
        if (gmVinModelDTO == null || StringUtils.isBlank(mainGroupCode)) {
            return Collections.emptyList();
        }
        List<SubjectDescEntity> subjectDescEntities = gmGroupDAO.querySubGroupByMGAndCatalog(gmVinModelDTO.getCatalog(), mainGroupCode);
        subjectDescEntities.removeIf(d -> filterByVin(gmVinModelDTO, d));
        return subjectDescEntities.stream()
                .map(GMSEPCGroupTransformer.ENTITY_TO_DTO)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
    }

    private boolean filterByVin(GMVinModelDTO gmVinModelDTO, SubjectDescEntity subjectDescEntity) {
        String des = subjectDescEntity.getDescriptionCn();
        if (StringUtils.isEmpty(des)) {
            return false;
        }
        boolean yearBool = matchImageByYear(des, gmVinModelDTO);
        boolean seriesBodyBool = matchImageBySeriesAndBody(des, gmVinModelDTO);
        boolean optionBool = matchOptionCode(des, gmVinModelDTO);
        return yearBool || seriesBodyBool || optionBool;
    }

    private boolean matchOptionCode(String des, GMVinModelDTO gmVinModelDTO) {
        List<String> vinoptioncode = gmVinModelDTO.getOptionCodeList();
        Matcher matcher = optionCodeRex.matcher(des);
        boolean hasNoOption = true;
        while (matcher.find()) {
            //todo optimize
            hasNoOption = false;
            String group = matcher.group();
            group = StringUtils.replaceEach(group, new String[]{"(", ")"}, new String[]{"", ""});
            List<String> optionCode = Splitter.on(",").splitToList(group);
            boolean optionFlag = true;
            boolean nonOption = false;
            for (String code : optionCode) {
                if (!optionFlag) {
                    return false;
                }
                Matcher dealOptionCodeMatcher = dealOptionCodeRex.matcher(code);
                if (!dealOptionCodeMatcher.matches()) {
                    nonOption = true;
                    continue;
                }
                if (nonOption) {
                    nonOption = false;
                    optionFlag = vinoptioncode.contains(code);
                    continue;
                }
                if (code.contains("除") || code.contains("不包括")) {
                    String finalOptionCode = dealOptionCodeMatcher.group(0);
                    optionFlag = vinoptioncode.contains(finalOptionCode);
                    hasNoOption = !optionFlag;
                    continue;
                }
                optionFlag = !vinoptioncode.contains(code);
                if (!optionFlag) {
                    return false;
                }
            }
        }
        return !hasNoOption;
    }

    private boolean matchImageByYear(String des, GMVinModelDTO analyseResult) {
        boolean yearBool = false;
        String year = analyseResult.getYear();
        if (StringUtils.isEmpty(year)) {
            return false;
        }
        if (yearOne.matcher(des).matches()) {
            boolean start = Integer.parseInt(des.substring(0, 4)) <= Integer.parseInt(year);
            boolean end = Integer.parseInt(des.substring(5, 9)) >= Integer.parseInt(year);
            yearBool = !(start && end);
        } else if (yearTwo.matcher(des).matches()) {
            yearBool = !des.substring(0, 4).equals(year);
        }
        return yearBool;
    }

    private boolean matchImageBySeriesAndBody(String des, GMVinModelDTO analyseResult) {
        Boolean seriesBool = false;
        Boolean bodyBool = false;
        String series = StringUtils.EMPTY;
        String body = StringUtils.EMPTY;
        if (seriesTwo.matcher(des).matches()) {
            String lessYear = des.substring(10);
            int start = lessYear.indexOf(Pattern.compile("[^0-9]").matcher(lessYear).replaceAll("").trim().charAt(0));
            series = lessYear.substring(0, start);
            body = lessYear.substring(start, start + 2);
        } else if (seriesOne.matcher(des).matches()) {
            String lessYear = des.substring(10);
            int start = lessYear.indexOf(Pattern.compile("[^0-9]").matcher(lessYear).replaceAll("").trim().charAt(1));
            series = lessYear.substring(0, start);
            body = lessYear.substring(start, start + 2);
        } else if (seriesThree.matcher(des).matches()) {
            String lessYear = des.substring(10);
            series = lessYear.split(" ")[0];
        } else if (seriesFour.matcher(des).matches()) {
            //2004-2009 J19-35 前门饰件 (不包括31I)
            String lessYear = des.substring(10);
            Matcher bodyMatch = bodyRex.matcher(lessYear);
            if (bodyMatch.find()) {
                body = bodyMatch.group(0).trim();
            }
            String[] seriesResult = bodyMatch.replaceFirst("").split(" ");
            if (seriesResult.length > 0) {
                series = seriesResult[0];
            }
        }

        if (StringUtils.isNotEmpty(series)) {
            if (series.length() == 1) {
                seriesBool = analyseResult.getSeries().charAt(0) != series.charAt(0);
            } else if (series.length() > 1 && !series.contains("，") && !series.contains(" ")) {
                HashSet<String> seriesSet = Sets.newHashSet(series.split(","));
                if (seriesSet.stream().allMatch(d -> d.length() > 1)) {
                    seriesBool = !seriesSet.contains(analyseResult.getSeries());
                } else {
                    seriesBool = false;
                }
            } else if (series.length() > 1 && series.contains("，")) {
                HashSet<String> seriesSet = Sets.newHashSet(series.split("，"));
                if (seriesSet.stream().allMatch(d -> d.length() > 1)) {
                    seriesBool = !seriesSet.contains(analyseResult.getSeries());
                } else {
                    seriesBool = false;
                }
            } else if (series.length() > 1 && series.contains(",") && series.contains(" ")) {
                HashSet<String> seriesSet = Sets.newHashSet(series.split(", "));
                if (seriesSet.stream().allMatch(d -> d.length() > 1)) {
                    seriesBool = !seriesSet.contains(analyseResult.getSeries());
                } else {
                    seriesBool = false;
                }
            }
        } else {
            seriesBool = false;
        }
        if (StringUtils.isNotEmpty(body)) {
            List<String> bodyList = Splitter.on("-").splitToList(body);
            bodyBool = !bodyList.contains(analyseResult.getBody());
        }
        return bodyBool || seriesBool;
    }

    private List<SEPCGroupDTO> queryMGroup(GMVinModelDTO gmVinModelDTO) {
        String catalog = gmVinModelDTO.getCatalog();
        List<GroupDescEntity> groupDescEntities = gmGroupDAO.queryMGroupByCatalog(catalog);
        filterByEngineAndTransmision(groupDescEntities, gmVinModelDTO);
        return Lists2.transformNonNull(groupDescEntities, GMSEPCGroupTransformer.GROUP_TO_SEPC);
    }

    private void filterByEngineAndTransmision(List<GroupDescEntity> groupDescEntities, GMVinModelDTO gmVinModelDTO) {
        String engineDesc = gmVinModelDTO.getEngineDesc();
        String transmissionDesc = gmVinModelDTO.getTransmissionDesc();
        if (StringUtils.isBlank(engineDesc) || StringUtils.isBlank(transmissionDesc)) {
            return;
        }
        filterByEngine(engineDesc, groupDescEntities);
        filterByTransmission(transmissionDesc, groupDescEntities);
    }

    private void filterByTransmission(String transmissionDesc, List<GroupDescEntity> groupDescEntities) {
        List<String> transmission = Splitter.on(" ").trimResults().splitToList(transmissionDesc);
        if (CollectionUtils.isEmpty(transmission)) {
            return;
        }
        groupDescEntities.removeIf(d -> doFilterByTransmission(d, transmission));
    }

    private boolean doFilterByTransmission(GroupDescEntity groupDescEntity, List<String> transmission) {
        String descriptionCn = groupDescEntity.getDescriptionCn();
        if (!("04".equals(groupDescEntity.getGroupCode())
                && (descriptionCn.contains("自动")
                || descriptionCn.contains("手动")))) {
            return false;
        }
        String model = transmission.get(0).replace('器', '箱');
        descriptionCn = descriptionCn.replace('器', '箱');
        if (!descriptionCn.contains(model)) {
            return true;
        }
        if (transmission.size() == 1 || transmission.get(0).contains("自动")) {
            return false;
        }
        return !descriptionCn.contains(transmission.get(1));
    }

    private void filterByEngine(String engineDesc, List<GroupDescEntity> groupDescEntities) {
        List<String> engineList = Splitter.on(" ").trimResults().splitToList(engineDesc);
        Optional<String> engine = engineList.stream().filter(d -> d.contains("缸")).findFirst();
        if (!engine.isPresent()) {
            return;
        }
        groupDescEntities.removeIf(d -> doFilterByEngine(d, engine.get()));
    }

    private boolean doFilterByEngine(GroupDescEntity d, String engine) {
        if (!"00".equals(d.getGroupCode())) {
            return false;
        }
        return !d.getDescriptionCn().contains(engine);
    }

    @Override
    protected List<SEPCPartsDTO> queryPartsByVinCodeAndLastGroup(SEPCPartsQueryParam param) {
        String vinCode = param.getVinCode();
        GMVinModelDTO gmVinModelDTO = generalMotorsEPCAnalyser.analyseVinCode(vinCode);
        List<EPCPartsImageDTO> epcPartsImageDTOList = param.getGroupDTO().getEpcPartsImageDTOList();
        if (gmVinModelDTO == null || CollectionUtils.isEmpty(epcPartsImageDTOList) || StringUtils.isBlank(epcPartsImageDTOList.get(0).getImageCode())) {
            return Collections.emptyList();
        }
        String subject = epcPartsImageDTOList.get(0).getImageCode();
        List<GMPartsDetailDTO> gmPartsDetailDTOS = getGMParts(subject, gmVinModelDTO);
        if (CollectionUtils.isEmpty(gmPartsDetailDTOS)) {
            return Collections.emptyList();
        }
        gmPartsDetailDTOS = filterAndFillImageInfo(gmPartsDetailDTOS, gmVinModelDTO, subject);
        fillBtrPartsName(gmPartsDetailDTOS, gmVinModelDTO);
        return gmPartsDetailDTOS.stream()
                .map(GMSEPCPartsTransformer.GM_PARTS_TO_SEPC_PARTS)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
    }

    private void fillBtrPartsName(List<GMPartsDetailDTO> gmPartsDetailDTOS, GMVinModelDTO vinModelDTO) {
        if (CollectionUtils.isEmpty(gmPartsDetailDTOS)) {
            return;
        }
        List<String> partsCodes = Lists2.transformNonNull(gmPartsDetailDTOS, GMPartsDetailDTO::getPartsCode);
        List<GmPartsMappingDTO> gmPartsMappingDTOS = partsMappingDAO.queryByPartsCode(vinModelDTO.getBrandName(), partsCodes);
        Map<String, String> partsCode2BtrPartsname = gmPartsMappingDTOS.stream()
                .collect(Collectors.toMap(GmPartsMappingDTO::getPartsCode, GmPartsMappingDTO::getBtrPartsName, (v1, v2) -> v1 + "," + v2));
        gmPartsDetailDTOS.forEach(d -> d.setBtrPartsName(partsCode2BtrPartsname.getOrDefault(d.getPartsCode(), "")));
    }

    private List<GMPartsDetailDTO> filterAndFillImageInfo(List<GMPartsDetailDTO> gmPartsDetailDTOS, GMVinModelDTO vinModelDTO, String subject) {
        Set<String> position = Sets2.transformNonNull(gmPartsDetailDTOS, GMPartsDetailDTO::getPictureNO);
        if (CollectionUtils.isEmpty(position)) {
            return gmPartsDetailDTOS;
        }
        List<PartsPositionDTO> partsPositionDTOS = partsPositionDAO.queryImagePosition(vinModelDTO.getCatalog(), subject, position);
        if (CollectionUtils.isEmpty(partsPositionDTOS)) {
            return gmPartsDetailDTOS;
        }
        doFillImage(partsPositionDTOS, gmPartsDetailDTOS);
        List<GMPartsDetailDTO> noFilteredParts = Lists.newArrayList(gmPartsDetailDTOS);
        gmPartsDetailDTOS = generalMotorsEPCAnalyser.filterPartsInfoByVinAnalyseResult(gmPartsDetailDTOS, vinModelDTO);
        if (CollectionUtils.isEmpty(gmPartsDetailDTOS)) {
            return Collections.emptyList();
        }
        List<GMPartsDetailDTO> finalGmPartsDetailDTOS = gmPartsDetailDTOS;
        noFilteredParts.forEach(d -> {
            if (!d.containParts(finalGmPartsDetailDTOS)) {
                d.setPartsCode("");
            }
        });
        Set<String> filteredPosition = Sets2.transformNonNull(gmPartsDetailDTOS, GMPartsDetailDTO::getPictureNO);
        noFilteredParts.removeIf(d->filteredPosition.contains(d.getPictureNO()) && StringUtils.isBlank(d.getPartsCode()));
        return noFilteredParts;
    }

    private void doFillImage(List<PartsPositionDTO> partsPositionDTOS, List<GMPartsDetailDTO> gmPartsDetailDTOS) {
        if (CollectionUtils.isEmpty(partsPositionDTOS) || CollectionUtils.isEmpty(gmPartsDetailDTOS)) {
            return;
        }
        List<EPCPartsImagePositionDTO> epcPartsImagePositionDTOS = partsPositionDTOS.stream().map(this::buildEPCPosition).collect(Collectors.toList());
        Map<String, List<EPCPartsImagePositionDTO>> position2PositionInfo = epcPartsImagePositionDTOS.stream().collect(Collectors.groupingBy(EPCPartsImagePositionDTO::getPosition));
        gmPartsDetailDTOS.forEach(d -> {
            EPCPartsImageDTO epcPartsImageDTO = new EPCPartsImageDTO();
            epcPartsImageDTO.setImageCode(d.getSubject());
            epcPartsImageDTO.setImageUrl(d.getSubject());
            List<EPCPartsImagePositionDTO> positionDTOS = position2PositionInfo.get(d.getPictureNO());
            if (CollectionUtils.isNotEmpty(positionDTOS)) {
                epcPartsImageDTO.setEpcPartsImagePositionDTOs(positionDTOS);
            }
            d.setEpcPartsImageDTO(Lists.newArrayList(epcPartsImageDTO));
        });
    }

    private EPCPartsImagePositionDTO buildEPCPosition(PartsPositionDTO d) {
        EPCPartsImagePositionDTO epcPartsImagePositionDTO = new EPCPartsImagePositionDTO();
        epcPartsImagePositionDTO.setPosition(d.getPositionNo());
        epcPartsImagePositionDTO.setTopLeft(new EPCImagePointDTO(Integer.parseInt(d.getPositionX()), Integer.parseInt(d.getPositionY())));
        epcPartsImagePositionDTO.setBottomRight(new EPCImagePointDTO(Integer.parseInt(d.getPositionX()) + OFFSET, Integer.parseInt(d.getPositionY()) + OFFSET));
        return epcPartsImagePositionDTO;
    }

    private List<GMPartsDetailDTO> getGMParts(String subject, GMVinModelDTO gmVinModelDTO) {
        return partsDetailDAO.queryPartsBySubject(subject, gmVinModelDTO.getCatalog());
    }

    @Override
    protected List<SEPCSearchPartsDTO> queryPartsByVinCodeAndParts(SEPCPartsQueryParam param) {
        String vinCode = param.getVinCode();
        SEPCPartsDTO partsDTO = param.getPartsDTO();
        if (partsDTO == null || StringUtils.isBlank(partsDTO.getPartsCode())) {
            return Collections.emptyList();
        }
        GMVinModelDTO gmVinModelDTO = generalMotorsEPCAnalyser.analyseVinCode(vinCode);
        if (gmVinModelDTO == null) {
            return Collections.emptyList();
        }
        String partsCode = partsDTO.getPartsCode();
        List<GMPartsDetailDTO> partsDetails = partsDetailDAO.findPartsDetails(gmVinModelDTO.getCatalog(), Lists.newArrayList(partsCode));
        if (CollectionUtils.isEmpty(partsDetails)) {
            return Collections.emptyList();
        }

        return partsDetails.stream()
                .map(GMSEPCPartsTransformer.PARTS_TO_SEARCH_PARTS)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
    }

    private void fillEPCImage(List<GMPartsDetailDTO> partsDetails, GMVinModelDTO gmVinModelDTO) {
        if (CollectionUtils.isEmpty(partsDetails)) {
            return;
        }
        Set<String> pictureOn = partsDetails.stream().map(GMPartsDetailDTO::getPictureNO).collect(Collectors.toSet());
        Set<String> subject = partsDetails.stream().map(GMPartsDetailDTO::getSubject).collect(Collectors.toSet());
        List<PartsPositionDTO> partsPositionDTOS = partsPositionDAO.queryImagePositionByParts(gmVinModelDTO.getCatalog(), subject, pictureOn);
        if (CollectionUtils.isEmpty(partsPositionDTOS)) {
            return;
        }
        doFillImage(partsPositionDTOS, partsDetails);
    }

    @Override
    protected List<SEPCSearchGroupDTO> queryGroupsByVinCodeAndParts(SEPCPartsQueryParam param) {
        String vinCode = param.getVinCode();
        SEPCPartsDTO partsDTO = param.getPartsDTO();
        if (partsDTO == null || StringUtils.isBlank(partsDTO.getPartsCode())) {
            return Collections.emptyList();
        }
        GMVinModelDTO gmVinModelDTO = generalMotorsEPCAnalyser.analyseVinCode(vinCode);
        if (gmVinModelDTO == null) {
            return Collections.emptyList();
        }
        String partsCode = partsDTO.getPartsCode();
        List<GMPartsDetailDTO> partsDetails = partsDetailDAO.findPartsDetails(gmVinModelDTO.getCatalog(), Lists.newArrayList(partsCode));
        partsDetails.removeIf(d -> !partsDTO.getEpcName().equals(d.getPartNameCN()));
        if (CollectionUtils.isEmpty(partsDetails)) {
            return Collections.emptyList();
        }

        findAndFillSubGroupInfo(partsDetails, gmVinModelDTO);
        findAndFillMGroup(partsDetails, gmVinModelDTO);
        fillEPCImage(partsDetails, gmVinModelDTO);
        return partsDetails.stream()
                .map(GMSEPCPartsTransformer.PARTS_TO_SEARCH_GROUP)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
    }

    private void fillExitEPCImage(List<GMPartsDetailDTO> partsDetails, List<EPCPartsImageDTO> epcPartsImageDTOs) {
        if (CollectionUtils.isEmpty(epcPartsImageDTOs)) {
            return;
        }
        partsDetails.forEach(d -> d.setEpcPartsImageDTO(epcPartsImageDTOs));
    }

    private void findAndFillMGroup(List<GMPartsDetailDTO> partsDetails, GMVinModelDTO gmVinModelDTO) {
        if (CollectionUtils.isEmpty(partsDetails)) {
            return;
        }
        Set<String> groupCodes = Sets2.transformNonNull(partsDetails, GMPartsDetailDTO::getGroupCode);
        if (CollectionUtils.isEmpty(groupCodes)) {
            return;
        }
        List<GroupDescEntity> groupDescEntities = gmGroupDAO.queryMGroupByCatalogAndGroup(gmVinModelDTO.getCatalog(), groupCodes);
        if (CollectionUtils.isEmpty(groupDescEntities)) {
            return;
        }
        filterByEngineAndTransmision(groupDescEntities, gmVinModelDTO);
        Map<String, String> mainGroupMap = groupDescEntities.stream()
                .collect(Collectors.toMap(GroupDescEntity::getGroupCode, GroupDescEntity::getDescriptionCn, (v1, v2) -> v1));
        partsDetails.forEach(d -> d.setMainGroup(mainGroupMap.getOrDefault(d.getGroupCode(), "")));
    }

    private void findAndFillSubGroupInfo(List<GMPartsDetailDTO> partsDetails, GMVinModelDTO gmVinModelDTO) {
        Set<String> subjects = partsDetails.stream().map(GMPartsDetailDTO::getSubject).collect(Collectors.toSet());
        List<SubjectDescEntity> subjectDescEntities = gmGroupDAO.querySubGroupByPartsAndCatalog(gmVinModelDTO.getCatalog(), subjects);
        if (CollectionUtils.isEmpty(subjectDescEntities)) {
            return;
        }
        subjectDescEntities.removeIf(d -> filterByVin(gmVinModelDTO, d));
        Map<String, String> subjectMap = subjectDescEntities.stream()
                .collect(Collectors.toMap(SubjectDescEntity::getSubject, SubjectDescEntity::getDescriptionCn, (v1, v2) -> v1));
        partsDetails.forEach(d -> d.setSubGroup(subjectMap.getOrDefault(d.getSubject(), "")));
    }
}
